"""  
结果收集和报告生成模块  

负责收集命令执行结果，生成结构化报告  
提供多种格式的报告输出和统计分析功能  
"""  

import json  
import os  
from datetime import datetime  
from typing import List, Optional, Dict, Any  
from dataclasses import dataclass, field, asdict  
import logging  

# 获取当前模块的日志记录器  
logger = logging.getLogger(__name__)  


@dataclass  
class CommandResult:  
    """  
    单个命令的执行结果数据类  
    
    存储在单个主机上执行单条命令的完整结果信息  
    包括执行状态、输出内容、时间统计等  
    """  
    host_name: str          # 执行命令的主机名称  
    command: str            # 执行的命令内容  
    exit_code: int          # 命令退出状态码（0表示成功）  
    stdout: str             # 标准输出内容  
    stderr: str             # 标准错误输出内容  
    success: bool           # 命令是否执行成功  
    start_time: datetime    # 命令开始执行时间  
    end_time: datetime      # 命令结束执行时间  
    
    def to_dict(self) -> Dict[str, Any]:  
        """  
        将命令结果转换为字典格式  
        
        用于JSON序列化和报告生成  
        
        Returns:  
            Dict[str, Any]: 包含所有字段的字典  
        """  
        # 转换为基础字典  
        data = asdict(self)  
        
        # 将datetime对象转换为ISO格式字符串  
        data['start_time'] = self.start_time.isoformat()  
        data['end_time'] = self.end_time.isoformat()  
        
        # 计算执行耗时（秒）  
        data['duration'] = (self.end_time - self.start_time).total_seconds()  
        
        # 添加输出长度统计  
        data['stdout_length'] = len(self.stdout)  
        data['stderr_length'] = len(self.stderr)  
        
        # 添加成功状态的文本描述  
        data['status'] = "SUCCESS" if self.success else "FAILED"  
        
        return data  
    
    def get_summary(self) -> str:  
        """  
        获取命令执行结果的简要摘要  
        
        Returns:  
            str: 结果摘要字符串  
        """  
        duration = (self.end_time - self.start_time).total_seconds()  
        status = "成功" if self.success else f"失败(退出码:{self.exit_code})"  
        return f"[{self.host_name}] {self.command} - {status} ({duration:.2f}s)"  


@dataclass  
class TaskResult:  
    """  
    任务执行结果数据类  
    
    存储单个任务在所有指定主机上的执行结果  
    包含多个CommandResult和任务级别的统计信息  
    """  
    task_name: str                                          # 任务名称  
    start_time: datetime                                    # 任务开始时间  
    end_time: Optional[datetime] = None                     # 任务结束时间  
    success: bool = False                                   # 任务整体是否成功  
    command_results: List[CommandResult] = field(default_factory=list)  # 所有命令结果  
    
    def to_dict(self) -> Dict[str, Any]:  
        """  
        将任务结果转换为字典格式  
        
        Returns:  
            Dict[str, Any]: 包含任务信息和统计数据的字典  
        """  
        # 计算统计信息  
        total_commands = len(self.command_results)  
        successful_commands = sum(1 for r in self.command_results if r.success)  
        failed_commands = total_commands - successful_commands  
        
        # 计算平均执行时间  
        if self.command_results:  
            avg_duration = sum(  
                (r.end_time - r.start_time).total_seconds()   
                for r in self.command_results  
            ) / total_commands  
        else:  
            avg_duration = 0  
        
        return {  
            'task_name': self.task_name,  
            'start_time': self.start_time.isoformat(),  
            'end_time': self.end_time.isoformat() if self.end_time else None,  
            'duration': (self.end_time - self.start_time).total_seconds() if self.end_time else 0,  
            'success': self.success,  
            'status': "SUCCESS" if self.success else "FAILED",  
            
            # 统计信息  
            'statistics': {  
                'total_commands': total_commands,  
                'successful_commands': successful_commands,  
                'failed_commands': failed_commands,  
                'success_rate': f"{(successful_commands/max(total_commands,1)*100):.1f}%",  
                'average_duration': f"{avg_duration:.2f}s"  
            },  
            
            # 主机信息  
            'hosts': list(set(r.host_name for r in self.command_results)),  
            'host_count': len(set(r.host_name for r in self.command_results)),  
            
            # 详细的命令结果  
            'command_results': [r.to_dict() for r in self.command_results]  
        }  
    
    def get_failed_commands(self) -> List[CommandResult]:  
        """  
        获取所有失败的命令结果  
        
        Returns:  
            List[CommandResult]: 失败的命令结果列表  
        """  
        return [r for r in self.command_results if not r.success]  
    
    def get_host_summary(self) -> Dict[str, Dict[str, Any]]:  
        """  
        按主机汇总命令执行情况  
        
        Returns:  
            Dict[str, Dict[str, Any]]: 主机名到执行摘要的映射  
        """  
        host_summary = {}  
        
        for result in self.command_results:  
            host_name = result.host_name  
            if host_name not in host_summary:  
                host_summary[host_name] = {  
                    'total': 0,  
                    'success': 0,  
                    'failed': 0,  
                    'total_duration': 0.0  
                }  
            
            summary = host_summary[host_name]  
            summary['total'] += 1  
            summary['total_duration'] += (result.end_time - result.start_time).total_seconds()  
            
            if result.success:  
                summary['success'] += 1  
            else:  
                summary['failed'] += 1  
        
        # 计算成功率和平均耗时  
        for host_name, summary in host_summary.items():  
            summary['success_rate'] = f"{(summary['success']/summary['total']*100):.1f}%"  
            summary['avg_duration'] = f"{(summary['total_duration']/summary['total']):.2f}s"  
        
        return host_summary  


class ResultCollector:  
    """  
    结果收集和报告生成器  
    
    功能：  
    1. 收集和整理任务执行结果  
    2. 生成多种格式的执行报告  
    3. 提供详细的统计分析  
    4. 支持自定义报告模板  
    5. 实时结果展示和摘要输出  
    """  
    
    def __init__(self, report_dir: str = "reports"):  
        """  
        初始化结果收集器  
        
        Args:  
            report_dir: 报告文件保存目录  
        """  
        self.report_dir = report_dir  
        self._ensure_dir_exists()  
        
        # 收集统计信息  
        self.collection_stats = {  
            'reports_generated': 0,  
            'total_tasks_processed': 0,  
            'total_commands_processed': 0  
        }  
    
    def _ensure_dir_exists(self):  
        """  
        确保报告目录存在  
        
        如果目录不存在则创建，包括必需的父目录  
        """  
        try:  
            os.makedirs(self.report_dir, exist_ok=True)  
            logger.debug(f"报告目录已准备: {self.report_dir}")  
        except Exception as e:  
            logger.error(f"创建报告目录失败: {str(e)}")  
            raise  
    
    def generate_report(self, results: List[TaskResult], report_name: str = None) -> str:  
        """  
        生成JSON格式的执行报告  
        
        Args:  
            results: 任务执行结果列表  
            report_name: 自定义报告文件名，如果为None则自动生成  
            
        Returns:  
            str: 生成的报告文件路径  
            
        Raises:  
            Exception: 报告生成失败  
        """  
        # 生成报告文件名  
        if not report_name:  
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")  
            report_name = f"validation_report_{timestamp}.json"  
        
        # 确保文件名以.json结尾  
        if not report_name.endswith('.json'):  
            report_name += '.json'  
        
        report_path = os.path.join(self.report_dir, report_name)  
        
        try:  
            # 生成完整的报告数据结构  
            report_data = {  
                'metadata': {  
                    'report_version': '1.0',  
                    'generated_at': datetime.now().isoformat(),  
                    'generator': 'Ubuntu Environment Validator',  
                    'report_type': 'validation_report'  
                },  
                
                # 执行摘要  
                'summary': self._generate_summary(results),  
                
                # 详细的任务结果  
                'tasks': [task_result.to_dict() for task_result in results],  
                
                # 全局统计  
                'statistics': self._generate_detailed_statistics(results)  
            }  
            
            # 写入JSON文件  
            with open(report_path, 'w', encoding='utf-8') as f:  
                json.dump(report_data, f, indent=2, ensure_ascii=False)  
            
            # 更新统计  
            self.collection_stats['reports_generated'] += 1  
            self.collection_stats['total_tasks_processed'] += len(results)  
            self.collection_stats['total_commands_processed'] += sum(  
                len(task.command_results) for task in results  
            )  
            
            logger.info(f"报告已生成: {report_path}")  
            return report_path  
            
        except Exception as e:  
            logger.error(f"生成报告失败: {str(e)}")  
            raise  
    
    def _generate_summary(self, results: List[TaskResult]) -> Dict[str, Any]:  
        """  
        生成执行结果的汇总信息  
        
        Args:  
            results: 任务执行结果列表  
            
        Returns:  
            Dict[str, Any]: 汇总统计信息  
        """  
        if not results:  
            return {  
                'total_tasks': 0,  
                'successful_tasks': 0,  
                'failed_tasks': 0,  
                'task_success_rate': '0%',  
                'total_commands': 0,  
                'successful_commands': 0,  
                'failed_commands': 0,  
                'command_success_rate': '0%',  
                'total_duration': '0s',  
                'execution_start': None,  
                'execution_end': None  
            }  
        
        # 任务级别统计  
        total_tasks = len(results)  
        successful_tasks = sum(1 for r in results if r.success)  
        failed_tasks = total_tasks - successful_tasks  
        
        # 命令级别统计  
        total_commands = sum(len(r.command_results) for r in results)  
        successful_commands = sum(  
            sum(1 for c in r.command_results if c.success)   
            for r in results  
        )  
        failed_commands = total_commands - successful_commands  
        
        # 时间统计  
        start_times = [r.start_time for r in results]  
        end_times = [r.end_time for r in results if r.end_time]  
        
        if start_times and end_times:  
            execution_start = min(start_times)  
            execution_end = max(end_times)  
            total_duration = (execution_end - execution_start).total_seconds()  
        else:  
            execution_start = execution_end = None  
            total_duration = 0  
        
        return {  
            # 任务统计  
            'total_tasks': total_tasks,  
            'successful_tasks': successful_tasks,  
            'failed_tasks': failed_tasks,  
            'task_success_rate': f"{(successful_tasks/total_tasks*100):.1f}%" if total_tasks > 0 else "0%",  
            
            # 命令统计  
            'total_commands': total_commands,  
            'successful_commands': successful_commands,  
            'failed_commands': failed_commands,  
            'command_success_rate': f"{(successful_commands/total_commands*100):.1f}%" if total_commands > 0 else "0%",  
            
            # 时间统计  
            'total_duration': f"{total_duration:.2f}s",  
            'average_task_duration': f"{(total_duration/total_tasks):.2f}s" if total_tasks > 0 else "0s",  
            'execution_start': execution_start.isoformat() if execution_start else None,  
            'execution_end': execution_end.isoformat() if execution_end else None  
        }  
    
    def _generate_detailed_statistics(self, results: List[TaskResult]) -> Dict[str, Any]:  
        """  
        生成详细的统计分析信息  
        
        Args:  
            results: 任务执行结果列表  
            
        Returns:  
            Dict[str, Any]: 详细统计信息  
        """  
        if not results:  
            return {}  
        
        # 主机统计  
        all_hosts = set()  
        host_success_count = {}  
        host_failure_count = {}  
        
        for task_result in results:  
            for cmd_result in task_result.command_results:  
                host = cmd_result.host_name  
                all_hosts.add(host)  
                
                if host not in host_success_count:  
                    host_success_count[host] = 0  
                    host_failure_count[host] = 0  
                
                if cmd_result.success:  
                    host_success_count[host] += 1  
                else:  
                    host_failure_count[host] += 1  
        
        # 任务类型统计（串行vs并行）  
        serial_tasks = 0  
        parallel_tasks = 0  
        tasks_with_dependencies = 0  
        
        for task_result in results:  
            # 这里需要从任务配置中获取信息，暂时无法准确统计  
            # 在实际实现中，可以在TaskResult中添加更多元数据  
            pass  
        
        # 失败分析  
        failure_analysis = self._analyze_failures(results)  
        
        return {  
            'host_statistics': {  
                'total_hosts': len(all_hosts),  
                'host_list': list(all_hosts),  
                'host_success_rates': {  
                    host: f"{(host_success_count[host]/(host_success_count[host]+host_failure_count[host])*100):.1f}%"  
                    for host in all_hosts  
                }  
            },  
            'failure_analysis': failure_analysis,  
            'performance_metrics': self._calculate_performance_metrics(results)  
        }  
    
    def _analyze_failures(self, results: List[TaskResult]) -> Dict[str, Any]:  
        """  
        分析失败原因和模式  
        
        Args:  
            results: 任务执行结果列表  
            
        Returns:  
            Dict[str, Any]: 失败分析结果  
        """  
        failed_tasks = [r for r in results if not r.success]  
        all_failed_commands = []  
        
        for task_result in results:  
            all_failed_commands.extend(task_result.get_failed_commands())  
        
        # 按退出码分类失败  
        exit_code_stats = {}  
        for cmd in all_failed_commands:  
            code = cmd.exit_code  
            if code not in exit_code_stats:  
                exit_code_stats[code] = 0  
            exit_code_stats[code] += 1  
        
        # 按主机分类失败  
        host_failure_stats = {}  
        for cmd in all_failed_commands:  
            host = cmd.host_name  
            if host not in host_failure_stats:  
                host_failure_stats[host] = 0  
            host_failure_stats[host] += 1  
        
        return {  
            'failed_task_count': len(failed_tasks),  
            'failed_command_count': len(all_failed_commands),  
            'exit_code_distribution': exit_code_stats,  
            'host_failure_distribution': host_failure_stats,  
            'common_errors': self._extract_common_errors(all_failed_commands)  
        }  
    
    def _extract_common_errors(self, failed_commands: List[CommandResult]) -> List[Dict[str, Any]]:  
        """  
        提取常见错误模式  
        
        Args:  
            failed_commands: 失败的命令结果列表  
            
        Returns:  
            List[Dict[str, Any]]: 常见错误列表  
        """  
        error_patterns = {}  
        
        for cmd in failed_commands:  
            # 简化错误信息，提取关键词  
            if cmd.stderr:  
                # 提取错误的前50个字符作为错误模式  
                error_key = cmd.stderr.strip()[:50]  
                if error_key not in error_patterns:  
                    error_patterns[error_key] = {  
                        'count': 0,  
                        'hosts': set(),  
                        'commands': set()  
                    }  
                error_patterns[error_key]['count'] += 1  
                error_patterns[error_key]['hosts'].add(cmd.host_name)  
                error_patterns[error_key]['commands'].add(cmd.command)  
        
        # 转换为列表并排序  
        common_errors = []  
        for error, stats in sorted(error_patterns.items(), key=lambda x: x[1]['count'], reverse=True):  
            common_errors.append({  
                'error_pattern': error,  
                'occurrence_count': stats['count'],  
                'affected_hosts': list(stats['hosts']),  
                'affected_commands': list(stats['commands'])  
            })  
        
        return common_errors[:10]  # 返回前10个最常见的错误  
    
    def _calculate_performance_metrics(self, results: List[TaskResult]) -> Dict[str, Any]:  
        """  
        计算性能指标  
        
        Args:  
            results: 任务执行结果列表  
            
        Returns:  
            Dict[str, Any]: 性能指标  
        """  
        if not results:  
            return {}  
        
        # 收集所有命令的执行时间  
        durations = []  
        for task_result in results:  
            for cmd_result in task_result.command_results:  
                duration = (cmd_result.end_time - cmd_result.start_time).total_seconds()  
                durations.append(duration)  
        
        if not durations:  
            return {}  
        
        durations.sort()  
        
        return {  
            'command_execution_times': {  
                'min': f"{min(durations):.2f}s",  
                'max': f"{max(durations):.2f}s",  
                'average': f"{sum(durations)/len(durations):.2f}s",  
                'median': f"{durations[len(durations)//2]:.2f}s",  
                'p95': f"{durations[int(len(durations)*0.95)]:.2f}s"  
            },  
            'throughput': {  
                'commands_per_second': f"{len(durations)/max(sum(durations), 1):.2f}",  
                'total_commands': len(durations)  
            }  
        }  
    
    def print_summary(self, results: List[TaskResult]):  
        """  
        在控制台打印执行摘要  
        
        Args:  
            results: 任务执行结果列表  
        """  
        summary = self._generate_summary(results)  
        
        # 打印分隔线和标题  
        print("\n" + "="*60)  
        print("          执行摘要报告")  
        print("="*60)  
        
        # 任务统计  
        print("📊 任务执行统计:")  
        print(f"   ├─ 任务总数: {summary['total_tasks']}")  
        print(f"   ├─ 成功任务: {summary['successful_tasks']}")  
        print(f"   ├─ 失败任务: {summary['failed_tasks']}")  
        print(f"   └─ 任务成功率: {summary['task_success_rate']}")  
        
        print("-"*60)  
        
        # 命令统计  
        print("⚡ 命令执行统计:")  
        print(f"   ├─ 命令总数: {summary['total_commands']}")  
        print(f"   ├─ 成功命令: {summary['successful_commands']}")  
        print(f"   ├─ 失败命令: {summary['failed_commands']}")  
        print(f"   └─ 命令成功率: {summary['command_success_rate']}")  
        
        print("-"*60)  
        
        # 时间统计  
        print("⏱️  执行时间统计:")  
        print(f"   ├─ 总耗时: {summary['total_duration']}")  
        print(f"   ├─ 平均任务耗时: {summary['average_task_duration']}")  
        if summary['execution_start']:  
            print(f"   ├─ 开始时间: {summary['execution_start']}")  
            print(f"   └─ 结束时间: {summary['execution_end']}")  
        
        print("="*60)  
        
        # 打印失败详情  
        self._print_failure_details(results)  
    
    def _print_failure_details(self, results: List[TaskResult]):  
        """  
        打印失败命令的详细信息  
        
        Args:  
            results: 任务执行结果列表  
        """  
        # 收集所有失败的命令  
        failed_details = []  
        for task_result in results:  
            for cmd_result in task_result.command_results:  
                if not cmd_result.success:  
                    failed_details.append((task_result.task_name, cmd_result))  
        
        if not failed_details:  
            print("🎉 所有命令都执行成功！")  
            return  
        
        print(f"\n❌ 失败命令详情 ({len(failed_details)}个):")
        print("-"*60)
        
        for i, (task_name, cmd_result) in enumerate(failed_details, 1):
            duration = (cmd_result.end_time - cmd_result.start_time).total_seconds()
            
            print(f"[{i:2d}] 任务: {task_name}")
            print(f"     主机: {cmd_result.host_name}")
            print(f"     命令: {cmd_result.command}")
            print(f"     退出码: {cmd_result.exit_code}")
            print(f"     耗时: {duration:.2f}s")
            
            if cmd_result.stderr:
                # 限制错误信息长度，避免输出过长
                error_msg = cmd_result.stderr.strip()
                if len(error_msg) > 200:
                    error_msg = error_msg[:200] + "..."
                print(f"     错误: {error_msg}")
            
            if i < len(failed_details):  # 不是最后一个
                print("-"*40)
        
        print("="*60)
    
    def export_csv_summary(self, results: List[TaskResult], csv_path: str = None) -> str:
        """
        导出CSV格式的摘要报告
        
        Args:
            results: 任务执行结果列表
            csv_path: CSV文件路径，如果为None则自动生成
            
        Returns:
            str: 生成的CSV文件路径
        """
        import csv
        
        if not csv_path:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            csv_path = os.path.join(self.report_dir, f"summary_{timestamp}.csv")
        
        try:
            with open(csv_path, 'w', newline='', encoding='utf-8') as csvfile:
                fieldnames = [
                    'task_name', 'host_name', 'command', 'success', 
                    'exit_code', 'duration', 'start_time', 'end_time'
                ]
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                writer.writeheader()
                
                for task_result in results:
                    for cmd_result in task_result.command_results:
                        writer.writerow({
                            'task_name': task_result.task_name,
                            'host_name': cmd_result.host_name,
                            'command': cmd_result.command,
                            'success': cmd_result.success,
                            'exit_code': cmd_result.exit_code,
                            'duration': (cmd_result.end_time - cmd_result.start_time).total_seconds(),
                            'start_time': cmd_result.start_time.isoformat(),
                            'end_time': cmd_result.end_time.isoformat()
                        })
            
            logger.debug(f"CSV摘要报告已生成: {csv_path}")
            return csv_path
            
        except Exception as e:
            logger.error(f"生成CSV报告失败: {str(e)}")
            raise
    
    def get_collection_stats(self) -> Dict[str, Any]:
        """
        获取收集器的统计信息
        
        Returns:
            Dict[str, Any]: 收集器统计数据
        """
        return self.collection_stats.copy()
    
    def cleanup_old_reports(self, days_to_keep: int = 7):
        """
        清理旧的报告文件
        
        Args:
            days_to_keep: 保留最近几天的报告
        """
        try:
            import time
            from pathlib import Path
            
            cutoff_time = time.time() - (days_to_keep * 24 * 60 * 60)
            report_path = Path(self.report_dir)
            
            if not report_path.exists():
                return
            
            deleted_count = 0
            for file_path in report_path.glob("*.json"):
                if file_path.stat().st_mtime < cutoff_time:
                    file_path.unlink()
                    deleted_count += 1
                    logger.debug(f"删除旧报告: {file_path}")
            
            if deleted_count > 0:
                logger.info(f"清理了 {deleted_count} 个旧报告文件")
            
        except Exception as e:
            logger.warning(f"清理旧报告失败: {str(e)}")